package com.piece.module.flowable.service;

import com.piece.core.framework.exception.ServerException;
import com.piece.core.framework.support.bootstrap.DataTable;
import com.piece.core.framework.support.bootstrap.QueryTable;
import com.piece.core.framework.support.convert.Convert;
import com.piece.core.framework.util.collection.MapUtil;
import com.piece.core.framework.util.file.IoUtil;
import com.piece.core.framework.util.object.ObjectUtil;
import com.piece.core.framework.util.string.StringUtil;
import com.piece.module.flowable.constants.ExceptionConstants;
import com.piece.module.flowable.constants.ProcessConstants;
import com.piece.module.flowable.dto.WfDeployment;
import com.piece.module.flowable.enums.StateType;
import com.piece.module.flowable.factory.FlowServiceFactory;
import com.piece.module.flowable.model.WfDeployForm;
import com.piece.module.flowable.repository.WfDeployFormRepository;
import com.piece.module.flowable.util.FlowUtil;
import org.apache.commons.io.IOUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.StartEvent;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.DeploymentBuilder;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.flowable.image.impl.DefaultProcessDiagramGenerator;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Transactional
public class WfDefinitionService extends FlowServiceFactory {

    @Resource
    private WfDeployFormRepository wfDeployFormRepository;

    /**
     * 查询流程定义列表
     *
     * @param queryTable 查询条件
     * @param processKey 指定流程
     */
    public DataTable<WfDeployment> findDefinitionPage(QueryTable queryTable, String processKey) {
        DataTable dataTable = new DataTable(queryTable);
        // 流程定义列表数据查询
        ProcessDefinitionQuery processDefinitionQuery;
        if (StringUtil.isNotEmpty(processKey)) {
            processDefinitionQuery = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionKey(processKey)
                    .orderByProcessDefinitionVersion().asc();
        } else {
            processDefinitionQuery = repositoryService.createProcessDefinitionQuery()
                    .latestVersion()
                    .orderByProcessDefinitionKey().asc();
        }

        Map<String, Object> params = queryTable.getParams();
        if (ObjectUtil.isNotEmpty(params)) {
            Set<Map.Entry<String, Object>> entries = params.entrySet();
            String paramName;
            for (Map.Entry<String, Object> entry : entries) {
                paramName = entry.getKey();
                if (paramName.startsWith("Q_")) {
                    paramName = paramName.split("_")[1];
                    switch (paramName) {
                        case "processKey":
                            processDefinitionQuery.processDefinitionKeyLike("%" + Convert.toStr(entry.getValue() + "%"));
                            break;
                        case "processName":
                            processDefinitionQuery.processDefinitionNameLike("%" + Convert.toStr(entry.getValue()) + "%");
                            break;
                        case "category":
                            processDefinitionQuery.processDefinitionCategory(Convert.toStr(entry.getValue()));
                            break;
                        case "state":
                            if (StateType.ACTIVE.getType().equals(Convert.toInt(entry.getValue()))) {
                                processDefinitionQuery.active();
                            } else if (StateType.SUSPENDED.getType().equals(Convert.toInt(entry.getValue()))) {
                                processDefinitionQuery.suspended();
                            }
                            break;
                    }
                }
            }
        }

        long totalCount = processDefinitionQuery.count();
        if (totalCount <= 0) {
            return dataTable;
        }

        int offset = queryTable.getLength() * (queryTable.getCurrentPage() - 1);
        List<ProcessDefinition> definitionList = processDefinitionQuery.listPage(offset, queryTable.getLength());
        List<WfDeployment> deploymentList = definitionList.stream().map(definition -> {
            String deploymentId = definition.getDeploymentId();
            Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
            WfDeployment vo = new WfDeployment();
            vo.setDefinitionId(definition.getId());
            vo.setProcessKey(definition.getKey());
            vo.setProcessName(definition.getName());
            vo.setVersion(definition.getVersion());
            vo.setCategory(deployment.getCategory());
            vo.setSuspended(definition.isSuspended());
            // 流程部署信息
            vo.setDeploymentId(deploymentId);
            vo.setDeploymentTime(deployment.getDeploymentTime());
            // 流程表单信息
            try {
                WfDeployForm deployForm = getDefinitionForm(definition.getId(), deploymentId);
                vo.setFormId(deployForm.getFormId());
                vo.setFormName(deployForm.getFormName());
                vo.setFormContent(deployForm.getContent());
            } catch (Exception e) {
            }
            return vo;
        }).collect(Collectors.toList());

        dataTable.setData(deploymentList);
        dataTable.setRecordsTotal(Convert.toInt(totalCount));
        return dataTable;
    }

    /**
     * 获取定义节点表单
     */
    public WfDeployForm getDefinitionForm(String definitionId, String deployId) {
        InputStream inputStream = repositoryService.getProcessModel(definitionId);
        try {
            String bpmnString = IoUtil.toString(inputStream, StandardCharsets.UTF_8.name());
            BpmnModel bpmnModel = FlowUtil.getBpmnModel(bpmnString);
            StartEvent startEvent = FlowUtil.getStartEvent(bpmnModel);
            return wfDeployFormRepository.findOneByCondition(MapUtil.builder(new HashMap<String, Object>())
                    .put("deployId", deployId)
                    .put("formKey", startEvent.getFormKey())
                    .put("nodeKey", startEvent.getId())
                    .build());
        } catch (Exception e) {
            throw new ServerException(ExceptionConstants.MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG, null);
        }
    }

    /**
     * 获取XML文本文件
     */
    public String getDefinitionXml(String definitionId) throws IOException {
        InputStream inputStream = repositoryService.getProcessModel(definitionId);
        return IOUtils.toString(inputStream, StandardCharsets.UTF_8.name());
    }

    /**
     * 激活或挂起流程定义
     *
     * @param stateType    状态
     * @param definitionId 流程定义ID
     */
    public void changeDefinitionState(StateType stateType, String definitionId) {
        switch (stateType) {
            case ACTIVE:
                repositoryService.activateProcessDefinitionById(definitionId, true, null);
                break;
            case SUSPENDED:
                repositoryService.suspendProcessDefinitionById(definitionId, true, null);
                break;
        }
    }

    /**
     * 删除流程部署
     *
     * @param deploymentIds 流程部署ID集合
     */
    public void deleteDefinition(String[] deploymentIds) {
        Arrays.stream(deploymentIds).forEach(deploymentId -> {
            // 允许级联删除 ,不设置会导致数据库外键关联异常
            repositoryService.deleteDeployment(deploymentId, true);
            wfDeployFormRepository.deleteByProperties("deployId", deploymentId);
        });
    }

    public boolean exist(String processDefinitionKey) {
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey(processDefinitionKey);
        long count = processDefinitionQuery.count();
        return count > 0;
    }

    /**
     * 导入流程文件
     */
    public void importBpmnFile(String name, String category, InputStream in) {
        String processName = name + ProcessConstants.BPMN_SUFFIX;
        // 创建流程部署
        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment()
                .name(processName)
                .key(name)
                .category(category)
                .addInputStream(processName, in);
        // 部署
        deploymentBuilder.deploy();
    }

    /**
     * 获取XML文本文件
     */
    public String readXml(String definitionId) throws IOException {
        InputStream inputStream = repositoryService.getProcessModel(definitionId);
        return IOUtils.toString(inputStream, StandardCharsets.UTF_8.name());
    }

    /**
     * 获取图片文件
     */
    public InputStream readImage(String definitionId) {
        DefaultProcessDiagramGenerator diagramGenerator = new DefaultProcessDiagramGenerator();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(definitionId);
        return diagramGenerator.generateDiagram(
                bpmnModel,
                "png",
                Collections.emptyList(),
                Collections.emptyList(),
                "宋体",
                "宋体",
                "宋体",
                null,
                1.0,
                false);
    }
}
